;////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
;//                                                                                                                            //
;//                                                         WNProject                                                          //
;//                                                                                                                            //
;//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
;//                                                                                                                            //
;////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

STACK_RESERVE   EQU 64 ; Space to reserve on the stack to save GPRs.

.CODE
ALIGN 8

SAVE_GPR MACRO
    sub rsp, STACK_RESERVE
   .ALLOCSTACK STACK_RESERVE
    mov [rsp + 56], r15
   .SAVEREG r15, 56
    mov [rsp + 48], r14
   .SAVEREG r14, 48
    mov [rsp + 40], r13
   .SAVEREG r13, 40
    mov [rsp + 32], r12
   .SAVEREG r12, 32
    mov [rsp + 24], rsi
   .SAVEREG rsi, 24
    mov [rsp + 16], rdi
   .SAVEREG rdi, 16
    mov [rsp + 8], rbx
   .SAVEREG rbx, 8
    mov [rsp], rbp
   .SAVEREG rbp, 0
   .ENDPROLOG
ENDM

RESTORE_GPR MACRO
    mov r15, [rsp + 56]
    mov r14, [rsp + 48]
    mov r13, [rsp + 40]
    mov r12, [rsp + 32]
    mov rsi, [rsp + 24]
    mov rdi, [rsp + 16]
    mov rbx, [rsp + 8]
    mov rbp, [rsp]
    add rsp, STACK_RESERVE
ENDM

SAVE_SSE MACRO _AddressRegister:REQ
    fxsave [_AddressRegister]            ; Save SSE registers
ENDM

RESTORE_SSE MACRO _AddressRegister:REQ
    fxrstor [_AddressRegister]           ; Restore SSE registers
    emms                                 ; Reset sse state
ENDM

BeginFiber PROC FRAME  ;, retptrloc:PTR, oldstackloc:PTR, inValue:PTR, func:PTR, stackloc:PTR, stacksize:DWORD, stackleft:DWORD
    ; retprtloc     - ecx
    ; oldstackloc   - edx
    ; inValue       - r8
    ; func          - r9
    SAVE_GPR
    mov rax, rsp
    mov r12, 15
    not r12
    and rax, r12
    mov r12, rsp
    sub r12, rax
    mov r13, r12 ;//r13 contains the "EXTRA" space that needs to be popped (because of sse needing to be stored on 16byte boundries)
    add r12, 512
    sub rsp, r12
    SAVE_SSE rsp
    push r13 ;//save off the "extra space"mov rax, rsp
    push gs:[10h]
    push gs:[8h]
    call _DUMMY_CALL
  _DUMMY_CALL:
    mov rax, [rsp] ; continueother for a description of this */
    add rax, 109
    mov [rcx], rax
    add rsp, 8
    push rbp
    mov [rdx], rsp
    mov rbx, r9
    add r13, 648 ;offset to stack thingy (r13 = stackloc)
    add r13, rsp ; r13 = stackloc
    mov r11, r13
    add r11, 8  ; r11 = *stacksize
    mov rcx, [r11] ;rcs = stacksize
    and rcx, 0FFFFFFFFh
    add r11, 8
    mov r11, [r11]
    mov rax, 0FFFFFFFFh ;stackleft
    and r11, rax
    mov rax, rsp
    sub rax, 16
    mov rsp, [r13]
    mov rdx, rsp
    sub rdx, r11 ;bottom of stack
    mov QWORD PTR gs:[10h],rdx
    add rdx, rcx ;top of stack
    mov QWORD PTR gs:[08h],rdx
    mov rcx, r8
    call rbx
    pop rsp
    pop rbp
    pop gs:[8h]
    pop gs:[10h]
    pop r13
    mov [rax], rax
    RESTORE_SSE rsp
    add rsp, r13
    add rsp, 512
    RESTORE_GPR
    mov rbp, 0
    ret
BeginFiber ENDP

SwitchFiber PROC FRAME ;, pushstacklocation:PTR, oldscriptptr:PTR, cont:QWORD, retpt:PTR, setrunnable:PTR, retloc:PTR, inValue:PTR, stacksize:DWORD, stackleft:DWORD
;//pushstacklocation --- rcx
;//oldscriptptr      --- rdx
;//cont              --- r8
;//retpt             --- r9
    SAVE_GPR
    mov rax, rsp
    mov r12, 15
    not r12
    and rax, r12
    mov r12, rsp
    sub r12, rax
    mov r13, r12 ;//r13 contains the "EXTRA" space that needs to be popped (because of sse needing to be stored on 16byte boundries)
    add r12, 512
    sub rsp, r12
    SAVE_SSE rsp
    push r13 ;//save off the "extra space"mov rax, rsp
    add r13, 624
    add r13, rsp; //R13 now contains setrunnablepointer
    mov rbx, rdx
    mov r10, 0FFFFFFFFh
    mov r12, [r13 + 32]
    and r12, r10
    mov r11, rbx
    sub r11, r12 ;bottom of stack
    mov QWORD PTR gs:[10h],r11
    mov r12, [r13 + 24]
    and r12, r10
    add r11, r12 ;top of stack
    mov QWORD PTR gs:[08h],r11
    mov [rcx], rsp; //Store our current stack into the correct location 
    call _DUMMY_CALL
  _DUMMY_CALL:
    mov rax, [rsp] ; continueother for a description of this */
    mov r12, [r13 + 8] ;//R13 = setrunnable
    add rax, 44; //offset our call-back location to right after our 'ret'
    mul r8   ;//multiply eax by 1 or 0 (are we coming back or not)
    mov [r12], rax
    mov rax, rsp
    sub rax, 16
    mov rcx, [r13 + 16]
    mov rsp, rbx
    mov r13, [r13]
    mov [r13], r13
    call r9
    pop rbp ;//this is the "crap" base pointer from the other thread
    pop r13; //need to get our offset back
    mov [rax], rax
    RESTORE_SSE rsp
    add rsp, r13
    add rsp, 512
    RESTORE_GPR
    ret
SwitchFiber ENDP

EndFiber PROC FRAME ;, pushstacklocation:PTR, oldscriptptr:PTR, cont:QWORD, retpt:PTR, setrunnable:PTR, retloc:PTR
;//pushstacklocation --- rcx
;//oldscriptptr      --- rdx
;//cont              --- r8
;//retval            --- r9
    SAVE_GPR
    mov rax, rsp
    mov r12, 15
    not r12
    and rax, r12
    mov r12, rsp
    sub r12, rax
    mov r13, r12 ;//r13 contains the "EXTRA" space that needs to be popped (because of sse needing to be stored on 16byte boundries)
    add r12, 512
    sub rsp, r12
    SAVE_SSE rsp
    push r13 ;//save off the "extra space"mov rax, rsp
    mov [rcx], rsp; //Store our current stack into the correct location 
    call _DUMMY_CALL
  _DUMMY_CALL:
    mov rax, [rsp] ; continueother for a description of this */
    push rdx 
    add r13, 640 ;576 for pushing SSE/GPR; 64 for the shadow space
    add r13, rsp
    mov r12, r13
    add r12, 8
    push r9
    add rax, 44; /offset our call-back location to right after our 'ret'
    mul r8   ;//multiply eax by 1 or 0 (are we coming back or not)
    mov r12, [r12]
    mov [r12], rax
    mov rax, [r13]
    ret 0
    pop rbp ;//this is the "crap" base pointer from the other thread
    pop r13; //need to get our offset back
    mov [rax], rax
    RESTORE_SSE rsp
    add rsp, r13
    add rsp, 512
    RESTORE_GPR
    ret
EndFiber ENDP

END